package org.slf4as.ext 
{
	import com.furusystems.data.Serializable;
	import flash.utils.Dictionary;
	import org.slf4as.helpers.Util;

	/**
	 * Base class for Event Data. Event Data contains data to be logged about an
	 * event. Users may extend this class for each EventType they want to log.
	 * 
	 * @author Ralph Goers
	 * @author Andreas Rønning
	 */
	public class EventData implements Serializable {
	  
		private static const serialVersionUID:int = 153270778642103985;
	  
		private var eventData:Dictionary = new Dictionary();
		public static const EVENT_MESSAGE:String  	= "EventMessage";
		public static const EVENT_TYPE:String  		= "EventType";
		public static const EVENT_DATETIME:String 	= "EventDateTime";
		public static const EVENT_ID:String  		= "EventId";

		/**
		* Default Constructor
		*/
		public function EventData(...args:Array) {
			if (args.length > 0) {
				if (args[0] is Dictionary) {
					fromDictionary(args[0] as Dictionary);
				}else if (args[0] is XML) {
					fromXML(args[0] as XML);
				}
			}
		}
		

		/**
		* Construct from a serialized form of the Map containing the RequestInfo
		* elements
		* 
		* @param xml
		*          The serialized form of the RequestInfo Map.
		*/
		private function fromXML(xml:XML):void 
		{
			try {
				//TODO: Map XML into eventData dict
				//this.eventData = (Map<String, Object>) decoder.readObject();
			} catch (e:Error) {
				throw new EventException("Error decoding " + xml);
			}
		}
		
		private function fromDictionary(d:Dictionary):void 
		{
			eventData = Util.cloneDictionary(d);
		}

		/**
		* Serialize all the EventData items into an XML representation.
		* 
		* @return an XML String containing all the EventDAta items.
		*/
		public function toXML():XML {
			return EventData.toXML(eventData);
		}

		/**
		* Serialize all the EventData items into an XML representation.
		* 
		* @return an XML String containing all the EventData items.
		*/
		public static function toXML(map:Dictionary):XML {
			//TODO: Serialize properly
			var out:XML = <data/>;
			for (var key:String in map) {
				var node:XML = <{key}/>;
				node.appendChild(map[key]);
				out.appendChild(node);
			}
			return out;
		}
		

		/**
		* Set the event identifier.
		* 
		* @param eventId
		*          The event identifier.
		*/
		public function set eventID(s:String):void {
			if (eventId == null) {
				throw new ArgumentError("eventId cannot be null");
			}
			eventData[EVENT_ID] = eventId;
		}

		/**
		* Retrieve the event identifier.
		* 
		* @return The event identifier
		*/
		public function get eventID():String {
			return String(eventData[EVENT_ID]);
		}
		
		/**
		* Retrieve the message text associated with this event, if any.
		* 
		* @return The message text associated with this event or null if there is
		*         none.
		*/
		public function get message():String {
			return String(eventData[EVENT_MESSAGE]);
		}

		/**
		* Set the message text associated with this event.
		* 
		* @param message
		*          The message text.
		*/
		public function set message(m:String) {
			eventData[EVENT_MESSAGE] = message;
		}

		/**
		* Retrieve the date and time the event occurred.
		* 
		* @return The Date associated with the event.
		*/
		public function get eventDateTime():Date {
			//TODO: Test
			return Date(eventData[EVENT_DATETIME]);
		}

		/**
		* Set the date and time the event occurred in case it is not the same as when
		* the event was logged.
		* 
		* @param eventDateTime
		*          The event Date.
		*/
		public function set eventDateTime(eventDateTime:Date) {
			eventData[EVENT_DATETIME] = eventDateTime;
		}

		/**
		* Set the type of event that occurred.
		* 
		* @param eventType
		*          The type of the event.
		*/
		public function set eventType(eventType:String) {
			eventData[EVENT_TYPE] = eventType;
		}

		/**
		* Retrieve the type of the event.
		* 
		* @return The event type.
		*/
		public function get eventType():String {
			return String(eventData[EVENT_TYPE]);
		}

		/**
		* Add arbitrary attributes about the event.
		* 
		* @param name
		*          The attribute's key.
		* @param obj
		*          The data associated with the key.
		*/
		public function put(String name, Serializable obj):Object {
			eventData[name] = obj;
			return eventData[name];
		}

		/**
		* Retrieve an event attribute.
		* 
		* @param name
		*          The attribute's key.
		* @return The value associated with the key or null if the key is not
		*         present.
		*/
		public function get(name:String):Serializable {
			return Serializable(eventData[name]);
		}

		/**
		* Populate the event data from a Map.
		* 
		* @param data
		*          The Map to copy.
		*/
		public function putAll(data:Dictionary):void {
			for (var key:* in data) {
				eventData[key] = data[key];
			}
		}

		/**
		* Returns the number of attributes in the EventData.
		* 
		* @return the number of attributes in the EventData.
		*/
		public function get size():int {
			var count:int = 0;
			for (var key:* in data) {
				count++;
			}
			return count;
		}

		/**
		* Returns an Iterator over all the entries in the EventDAta.
		* 
		* @return an Iterator that can be used to access all the event attributes.
		*/
		//public Iterator<Map.Entry<String, Object>> getEntrySetIterator() {
		//return this.eventData.entrySet().iterator();
		//}

		/**
		* Retrieve all the attributes in the EventData as a Map. Changes to this map
		* will be reflected in the EventData.
		* 
		* @return The Map of attributes in this EventData instance.
		*/
		public function get eventMap():Dictionary {
			return eventData;
		}

		/**
		* Convert the EventData to a String.
		* 
		* @return The EventData as a String.
		*/
		override public function toString():String {
			return String(toXML());
		}

		/**
		* Compare two EventData objects for equality.
		* 
		* @param o
		*          The Object to compare.
		* @return true if the objects are the same instance or contain all the same
		*         keys and their values.
		*/
		public function equals(o:Object):Boolean {
			if (this == o) {
				return true;
			}
			if (!(o instanceof EventData || o instanceof Dictionary)) {
				return false;
			}
			var map:Dictionary = (o instanceof EventData) ? EventData(o).getEventMap() : Dictionary(o);
			return eventData.equals(map);
		}

		/**
		* Compute the hashCode for this EventData instance.
		* 
		* @return The hashcode for this EventData instance.
		*/
		//@Override
		//public int hashCode() {
			//return this.eventData.hashCode();
		//}
	}
}